iT邦幫忙

2023 iThome 鐵人賽

DAY 18
0
Mobile Development

Spring Boot & Android Studio教學系列 第 18

掌握Spring Security:實現一個堅固的身份驗證與授權架構(EP1)

  • 分享至 

  • xImage
  •  

首先建立一個用戶資料的實體類以及Service與Repository方便後續認證使用

用戶實體建立

AppUserEntity.java

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "_user")
public class AppUserEntity implements UserDetails {
    @Id
    @GeneratedValue
    private Long id;
    @Column(unique = true)
    private String email;
    private String password;
    @Enumerated(EnumType.STRING)
    private AppUserRole userRole; // 使用者角色(ex: USER或ADMIN等)
    
    public AppUserEntity(String userName, String email, String password, AppUserRole userRole) {
        this.userName = userName;
        this.email = email;
        this.password = password;
        this.userRole = userRole;
    }

    /**
     * 用於提供該用戶授權權限集合
     * @return 用戶被授權的權限集合
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of(new SimpleGrantedAuthority(userRole.name()));
    }

    @Override
    public String getUsername() {
        return email;
    }

    @Override
    public String getPassword() {
        return password;
    }

    /**
     * 用來判斷使用者的帳戶是否過期
     * @return 如果帳戶已過期,返回false,表示使用者不應該被授權,反之則true。
     */
    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    /**
     * 用來判斷使用者的帳戶是否被鎖定
     * @return 如果帳戶被鎖定,返回false,表示使用者不應該被授權。
     */
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    /**
     * 用來判斷使用者的認證信息是否過期,例如密碼是否過期
     * @return 如果認證信息已過期,返回false,表示使用者不應該被授權。
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    /**
     * 用來判斷使用者是否啟用,如果使用者已被禁用
     * @return 返回false,表示使用者不應該被授權。
     */
    @Override
    public boolean isEnabled() {
        return true;
    }
}

UserDetails是一個 Spring Security 用於自定義用戶詳細信息的介面,它要求您實現一組方法,以描述用戶的身份、權限和帳戶狀態。

public enum AppUserRole {
    USER,
    ADMIN
}

AppUserService.java

@Service
@AllArgsConstructor
public class AppUserService implements UserDetailsService {
    private final static String USER_NOT_FOUND_MSG = "user with email %s not found";
    private final AppUserRepository appUserRepository;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
        return appUserRepository.findByEmail(email)
            .orElseThrow(
                () -> new UsernameNotFoundException(String.format(USER_NOT_FOUND_MSG, email))
            );
    }
}

UserDetailsService 的主要職責是通過用戶名查找用戶的詳細信息,然後進行密碼驗證,並根據用戶的權限配置進行授權。

AppUserRepository.java

@Repository
public interface AppUserRepository extends JpaRepository<AppUserEntity, Long> {
    Optional<AppUserEntity> findByEmail(String email);
}

Security 配置

接下來新增Security 一些配置

ApplicationConfig.java

@Configuration
@RequiredArgsConstructor
public class ApplicationConfig {

    private final AppUserRepository appUserRepository;

    /**
     * 用於查找用戶詳細信息
     * @return userEmail(用戶詳細信息)
     */
    @Bean
    public UserDetailsService userDetailsService() {
        return userEmail -> appUserRepository.findByEmail(userEmail)
                .orElseThrow(() -> new UsernameNotFoundException("User not found with email: " + userEmail));
    }

    /**
     * 執行身份驗證的關鍵組件
     */
    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService());
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

    /**
     * 管理身份驗證的組件
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }

    /**
     * 以便將用戶密碼進行安全的雜湊存儲
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

SecurityConfiguration

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable)  //禁止CSRF(跨站請求偽造)保護。
                .authorizeHttpRequests((authorize) -> authorize //對所有訪問HTTP端點的HttpServletRequest進行限制
                        .requestMatchers(
                                "/error/**",
                                "/api/auth/**"
                        ).permitAll()   //指定上述匹配規則中的路徑,允許所有用戶訪問,即不需要進行身份驗證。
                        .anyRequest().authenticated()   //其他尚未匹配到的路徑都需要身份驗證
                );
        return http.build();
    }
}

後續會介紹JWT


上一篇
保護您的 Spring 應用程序:Spring Security 逐步指南
下一篇
掌握Spring Security:使用JWT實現身份驗證(EP2)
系列文
Spring Boot & Android Studio教學30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言